#pragma once
#include "DataConversion.h"
#include <vcclr.h>
#include "MAIMPEqualizer.h"
#include "MAIMPConnectedPlaylist.h"
#include "IPlayListManager.h"
#include "IMAIMP2ControllerServices.h"
#include "CentralizedEventController.h"
#include "MAIMPMenuItem.h"
#include "IMAIMPMenuManager.h"
#include "MAIMPMenuManager.h"
#include "MAIMPOptionFrame.h"
#include "IMAIMPOptionFramesManager.h"
#include "MAIMPOptionFramesManager.h"
#include "IMAIMPPlayer.h"
#include "MAIMPManagedPlugin.h"

namespace dotNetInteropPlugin {
	namespace PluginDev {
		using namespace System;
		using namespace System::Runtime::InteropServices;



	template<class TConvAlloc>
	private ref class MAIMP2Controller: public MarshalByRefObject, public IMAIMPPlayer, public IMAIMPConfigurationManager, public IMAIMPLanguageManager, public IMAIMPEffects, public IMAIMPCurrentPlayingInfo, public IMAIMPPlayingOrderParams, public IMAIMPAdditionalParams, public IMAIMPCoverArtManager, public IMAIMPEventManager, public IMAIMP2ControllerServices, public System::IDisposable
	{
	private:
		typedef FileInfoManUnmanConverter<TConvAlloc> TemporyFInfConverter;
		typedef FileInfoManUnmanConverter<SystemMemoryAllocator> StableFInfConverter;
		typedef MAIMP2Controller<TConvAlloc> MAIMP2ControllerLocal;
	public:
		MAIMP2Controller(IAIMP2Controller* Ctrl, int PlgUID, int AppDomainID, bool CrossDmn, CentralizedEventController^ evCtrl)
		{
			inAIMPCtrl = Ctrl;
			inAIMPCtrl->AddRef();

			inLocalLinkToEventController = evCtrl;

			inAssocPluginUID = PlgUID;
			inDomainID = AppDomainID;
			inIsCrossDomain = CrossDmn;

			inEqualizerObj = gcnew MAIMPEqualizer(Ctrl);

			void* TmpPtr = NULL;
			Ctrl->AIMP_QueryObject(IAIMP2PlayerID, &TmpPtr);
			inPlayerCtrl = (IAIMP2Player*)TmpPtr;

			TmpPtr = NULL;
			Ctrl->AIMP_QueryObject(IAIMPConfigFileID, &TmpPtr);
			inConfigFileMan = (IAIMPConfigFile*)TmpPtr;

			TmpPtr = NULL;
			Ctrl->AIMP_QueryObject(IAIMPLanguageFileID, &TmpPtr);
			inLangFileMan = (IAIMPLanguageFile*)TmpPtr;

			TmpPtr = NULL;
			Ctrl->AIMP_QueryObject(IAIMP2ExtendedID, &TmpPtr);
			inAIMPExtended = (IAIMP2Extended*)TmpPtr;

			TmpPtr = NULL;
			Ctrl->AIMP_QueryObject(IAIMP2PlaylistManager2ID, &TmpPtr);
			inAIMPPlsMan = (IAIMP2PlaylistManager2*)TmpPtr;

			TmpPtr = NULL;
			Ctrl->AIMP_QueryObject(IAIMP2CoverArtManagerID, &TmpPtr);
			inCoverArtMan = (IAIMP2CoverArtManager*)TmpPtr;

			inPlsManager = gcnew MAIMPPlaylistManager(Ctrl, inAIMPPlsMan, inPlayerCtrl);
		
			inMenuManager = gcnew MAIMPMenuManager(Ctrl);

			inOptFrameManager = gcnew MAIMPOptionFramesManager(inAIMPExtended);
		}


		//================

		virtual void TestFunc()
		{
			//MAIMPOptionFrame^ optFrm = gcnew MAIMPOptionFrame();
			//optFrm->FrameName = "MyFrame";
			//optFrm->FrameCategory = AIMPOptionFramePosition::AIMP_FRAME_POS_SKINS;
			//optFrm->RegisterFrame(inAIMPExtended);
		}

		// ================ IMAIMPPlayer =========================
		property virtual Int32 ChanelBallance 
		{ 
			Int32 get()
			{
				return inAIMPCtrl->AIMP_Status_Get(AIMP_STS_BALANCE)-50;
			}
			void set(Int32 value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_BALANCE, value+50);
			}
		}
		property virtual Boolean IsMute 
		{
			Boolean get()
			{
				return inAIMPCtrl->AIMP_Status_Get(AIMP_STS_MUTE) != 0;
			}
			void set(Boolean value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_MUTE, value);
			}
		}
		property virtual UInt32 Volume
		{
			UInt32 get()
			{
				return inAIMPCtrl->AIMP_Status_Get(AIMP_STS_VOLUME);
			}
			void set(UInt32 value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_VOLUME, value);
			}
		}
		property virtual AIMPPlayingState PlayingState 
		{  
			AIMPPlayingState get()
			{
				return AIMPPlayingState(inAIMPCtrl->AIMP_Status_Get(AIMP_STS_Player));
			}
		}
		property virtual UInt32 Version
		{ 
			UInt32 get()
			{
				return inAIMPCtrl->AIMP_GetSystemVersion();
			}
		}
		property virtual IMAIMPConfigurationManager^ ConfigurationManager 
		{ 
			IMAIMPConfigurationManager^ get()
			{
				return this;
			}
		}
		property virtual IMAIMPLanguageManager^ LanguageManager
		{ 
			IMAIMPLanguageManager^ get()
			{
				return this;
			}
		}
		property virtual IMAIMPEffects^ Effects
		{ 
			IMAIMPEffects^ get()
			{
				return this;
			}
		}
		property virtual IMAIMPCurrentPlayingInfo^ CurrentPlayingInfo
		{ 
			IMAIMPCurrentPlayingInfo^ get()
			{
				return this;
			}
		}
		property virtual IMAIMPPlayingOrderParams^ PlayingOrderParams
		{ 
			IMAIMPPlayingOrderParams^ get()
			{
				return this;
			}
		}
		property virtual IMAIMPAdditionalParams^ AdditionalParams
		{ 
			IMAIMPAdditionalParams^ get()
			{
				return this;
			}
		}
		property virtual IMAIMPCoverArtManager^ CoverArtManager
		{ 
			IMAIMPCoverArtManager^ get()
			{
				return this;
			}
		}
		property virtual IMAIMPEqualizer^ Equalizer
		{ 
			IMAIMPEqualizer^ get()
			{
				return inEqualizerObj;
			}
		}
		property virtual IMAIMPPlaylistManager^ PlaylistManager
		{
			IMAIMPPlaylistManager^ get()
			{
				return inPlsManager;
			}
		}
		property virtual IMAIMPEventManager^ EventManager
		{
			IMAIMPEventManager^ get()
			{
				return this;
			}
		}
		property virtual IMAIMPMenuManager^ MenuManager
		{
			IMAIMPMenuManager^ get()
			{
				return inMenuManager;
			}
		}
		property virtual IMAIMPOptionFramesManager^ OptionFramesManager
		{
			IMAIMPOptionFramesManager^ get()
			{
				return inOptFrameManager;
			}
		}



		virtual void NextTrack()
		{
			inPlayerCtrl->NextTrack();
		}
		virtual void Pause()
		{
			inPlayerCtrl->Pause();
		}
		virtual void Play()
		{
			inPlayerCtrl->PlayOrResume();
		}
		virtual void PrevTrack()
		{
			inPlayerCtrl->PrevTrack();
		}
		virtual void Stop()
		{
			inPlayerCtrl->Stop();
		}

		// ================================================

		// ====================IMAIMPEffects============================

		property virtual UInt32 Reverberation
		{
			UInt32 get()
			{
				return inAIMPCtrl->AIMP_Status_Get(AIMP_STS_REVERB);
			}
			void set(UInt32 value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_REVERB, value);
			}
		}		
		property virtual UInt32 Echo
		{
			UInt32 get()
			{
				return inAIMPCtrl->AIMP_Status_Get(AIMP_STS_ECHO);
			}
			void set(UInt32 value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_ECHO, value);
			}
		}
		property virtual UInt32 Chorus
		{
			UInt32 get()
			{
				return inAIMPCtrl->AIMP_Status_Get(AIMP_STS_CHORUS);
			}
			void set(UInt32 value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_CHORUS, value);
			}
		}
		property virtual UInt32 Flanger
		{
			UInt32 get()
			{
				return inAIMPCtrl->AIMP_Status_Get(AIMP_STS_Flanger);
			}
			void set(UInt32 value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_Flanger, value);
			}
		}
		property virtual UInt32 Speed
		{
			UInt32 get()
			{
				return inAIMPCtrl->AIMP_Status_Get(AIMP_STS_SPEED)+50;
			}
			void set(UInt32 value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_SPEED, value-50);
			}
		}


		// ================================================

		// ===================IMAIMPCurrentPlayingInfo=============================

		property virtual TimeSpan Position
		{
			TimeSpan get()
			{
				return TimeSpan::FromSeconds(inAIMPCtrl->AIMP_Status_Get(AIMP_STS_POS));
			}
			void set(TimeSpan value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_POS, (UInt32)value.TotalSeconds);
			}
		}
		property virtual TimeSpan Length
		{
			TimeSpan get()
			{
				return TimeSpan::FromSeconds(inAIMPCtrl->AIMP_Status_Get(AIMP_STS_LENGTH));
			}
		}

		property virtual UInt32 KBPS
		{
			UInt32 get()
			{
				return inAIMPCtrl->AIMP_Status_Get(AIMP_STS_KBPS);
			}
		}
		property virtual UInt32 KHZ
		{
			UInt32 get()
			{
				return inAIMPCtrl->AIMP_Status_Get(AIMP_STS_KHZ);
			}
		}
		property virtual UInt32 ChanelNumber
		{
			UInt32 get()
			{
				return inAIMPCtrl->AIMP_Status_Get(AIMP_STS_MODE);
			}
		}

		property virtual Boolean IsRadioCapture
		{
			Boolean get()
			{
				return inAIMPCtrl->AIMP_Status_Get(AIMP_STS_RADIO) != 0;
			}
			void set(Boolean value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_RADIO, value);
			}
		}
		property virtual AIMPStreamType StreamType
		{
			AIMPStreamType get()
			{
				return AIMPStreamType(inAIMPCtrl->AIMP_Status_Get(AIMP_STS_STREAM_TYPE));
			}
			void set(AIMPStreamType value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_STREAM_TYPE, (UInt32)value);
			}
		}

		virtual MAIMPFileInfo^ GetCurrentTrackInfo()
		{
			MAIMPFileInfo^ res = nullptr;
			AIMP2FileInfo* prSt = TemporyFInfConverter::GetPreparedStructure();
			if (inAIMPCtrl->AIMP_GetCurrentTrack(prSt))
				res = TemporyFInfConverter::GetManagedFromUnmanaged(prSt);
			TemporyFInfConverter::DisposeStructure(prSt);
			return res;
		}

		// ================================================

		// ====================IMAIMPPlayingOrderParams============================

		property virtual Boolean IsRepeatTrack
		{
			Boolean get()
			{
				return inAIMPCtrl->AIMP_Status_Get(AIMP_STS_REPEAT) != 0;
			}
			void set(Boolean value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_REPEAT, value);
			}
		}
		property virtual AIMPOnPlaylistEndAction OnPlaylistEndAction
		{
			AIMPOnPlaylistEndAction get()
			{
				return (AIMPOnPlaylistEndAction)inAIMPCtrl->AIMP_Status_Get(AIMP_STS_REPEATPLS);
			}
			void set(AIMPOnPlaylistEndAction value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_REPEATPLS, (UInt32)value);
			}
		}
		property virtual Boolean IsNotRepeatePlaylistWithOneFile
		{
			Boolean get()
			{
				return inAIMPCtrl->AIMP_Status_Get(AIMP_STS_REP_PLS_1) != 0;
			}
			void set(Boolean value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_REP_PLS_1, value);
			}
		}
		property virtual Boolean IsShuffleMode
		{
			Boolean get()
			{
				return inAIMPCtrl->AIMP_Status_Get(AIMP_STS_SHUFFLE) != 0;
			}
			void set(Boolean value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_SHUFFLE, value);
			}
		}

		// ================================================

		// ===================IMAIMPAdditionalParams=============================

		property virtual Boolean IsOnTopMost
		{
			Boolean get()
			{
				return inAIMPCtrl->AIMP_Status_Get(AIMP_STS_ON_TOPMOST) != 0;
			}
			void set(Boolean value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_ON_TOPMOST, value);
			}
		}
		property virtual AIMPTimerDisplayStyle TimerDisplayStyle
		{
			AIMPTimerDisplayStyle get()
			{
				return AIMPTimerDisplayStyle(inAIMPCtrl->AIMP_Status_Get(AIMP_STS_TIMER));
			}
			void set(AIMPTimerDisplayStyle value)
			{
				inAIMPCtrl->AIMP_Status_Set(AIMP_STS_TIMER, (UInt32)value);
			}
		}

		virtual String^ GetPathTo(AIMPPathToFile elem)
		{
			const int AllocSize = 4*1024;
			PWCHAR AllocMem = (PWCHAR)TConvAlloc::Allocate(AllocSize);
			AllocMem[AllocSize/2 - 1] = 0;
			inAIMPExtended->AIMP_GetPath((Int32)elem, AllocMem, AllocSize/2 - 1);
			String^ res = gcnew String(AllocMem);
			TConvAlloc::Dispose((char*)AllocMem);
			return res;
		}

		virtual MAIMPFileInfo^ GetTrackInfoFromFile(String^ FilePath)
		{
			MAIMPFileInfo^ res = nullptr;
			pin_ptr<const WCHAR> strData = PtrToStringChars(FilePath);
			AIMP2FileInfo* prSt = TemporyFInfConverter::GetPreparedStructure();
			if (inAIMPCtrl->AIMP_QueryInfo((PWCHAR)strData, prSt))
				res = TemporyFInfConverter::GetManagedFromUnmanaged(prSt);
			TemporyFInfConverter::DisposeStructure(prSt);
			return res;
		}

		virtual void GetTrackInfoFromFile(MAIMPFileInfo^ Dest, String^ FilePath)
		{
			if (Dest != nullptr && FilePath != nullptr)
			{
				pin_ptr<const WCHAR> strData = PtrToStringChars(FilePath);
				AIMP2FileInfo* prSt = TemporyFInfConverter::GetPreparedStructure();
				if (inAIMPCtrl->AIMP_QueryInfo((PWCHAR)strData, prSt))
					TemporyFInfConverter::CopyToManagedFromUnmanaged(Dest, prSt);
				TemporyFInfConverter::DisposeStructure(prSt);
			}
		}
		virtual void GetTrackInfoFromFile(MAIMPFileInfo^ Dest)
		{
			if (Dest != nullptr && Dest->FileName != nullptr)
			{
				pin_ptr<const WCHAR> strData = PtrToStringChars(Dest->FileName);
				AIMP2FileInfo* prSt = TemporyFInfConverter::GetPreparedStructure();
				if (inAIMPCtrl->AIMP_QueryInfo((PWCHAR)strData, prSt))
					TemporyFInfConverter::CopyToManagedFromUnmanaged(Dest, prSt);
				TemporyFInfConverter::DisposeStructure(prSt);
			}
		}


		virtual String^ GetSupportedExtensions(AIMPExtensionType ExtType)
		{
			const int AllocSize = 4*1024;
			PWCHAR AllocMem = (PWCHAR)TConvAlloc::Allocate(AllocSize);
			AllocMem[AllocSize/2 - 1] = 0;
			inAIMPCtrl->AIMP_GetSupportExts((DWORD)ExtType, AllocMem, AllocSize/2 - 1);
			String^ res = gcnew String(AllocMem);
			TConvAlloc::Dispose((char*)AllocMem);
			return res;
		}


		virtual System::Windows::Forms::IWin32Window^ GetAIMPWindow(AIMPPlayerWindow WndType)
		{
			HWND Wnd = (HWND)inAIMPCtrl->AIMP_Status_Get((DWORD)WndType);
			return gcnew dotNetInteropPlugin::CSharpMixingLibrary::WinFormHandleWrapper(IntPtr(Wnd));
		}


		virtual void CallFunction(AIMPCallFunction cf)
		{
			inAIMPCtrl->AIMP_CallFunction((DWORD)cf);
		}

		// ================================================

		// =================== IMAIMPConfigurationManager =============================

		virtual Boolean IsSectionExist(String^ SectionName) = IMAIMPConfigurationManager::IsSectionExist
		{
			pin_ptr<const WCHAR> strData = PtrToStringChars(SectionName);
			HRESULT res = inConfigFileMan->AIMP_Config_IsSectionExists((PWCHAR)strData, SectionName->Length);
			return SUCCEEDED(res);
		}
		virtual Int32 ReadParameterInt32(String^ Section, String^ Name) = IMAIMPConfigurationManager::ReadParameterInt32
		{
			pin_ptr<const WCHAR> tmpMem = PtrToStringChars(Section);
			pin_ptr<const WCHAR> tmpMem2 = PtrToStringChars(Name);
			int res = 0;
			inConfigFileMan->AIMP_Config_ReadInteger((PWCHAR)tmpMem, (PWCHAR)tmpMem2, Section->Length, Name->Length, &res);
			return res;
		}
		virtual Boolean ReadParameterInt32(String^ Section, String^ Name, [Out] Int32% Val) = IMAIMPConfigurationManager::ReadParameterInt32
		{
			pin_ptr<const WCHAR> tmpMem = PtrToStringChars(Section);
			pin_ptr<const WCHAR> tmpMem2 = PtrToStringChars(Name);
			int res = 0;
			bool suc = inConfigFileMan->AIMP_Config_ReadInteger((PWCHAR)tmpMem, (PWCHAR)tmpMem2, Section->Length, Name->Length, &res);
			Val = res;
			return suc;
		}

		virtual String^ ReadParameterString(String^ Section, String^ Name) = IMAIMPConfigurationManager::ReadParameterString
		{
			const int AllocSize = 4*1024;
			pin_ptr<const WCHAR> tmpMem = PtrToStringChars(Section);
			pin_ptr<const WCHAR> tmpMem2 = PtrToStringChars(Name);
			PWCHAR AllocMem = (PWCHAR)TConvAlloc::Allocate(AllocSize);
			AllocMem[AllocSize/2 - 1] = 0;
			HRESULT hres = inConfigFileMan->AIMP_Config_ReadString((PWCHAR)tmpMem, (PWCHAR)tmpMem2, (PWCHAR)AllocMem, Section->Length, Name->Length, AllocSize/2 - 1);
			String^ res = nullptr;
			if (SUCCEEDED(hres))
				res = gcnew String(AllocMem);
			TConvAlloc::Dispose((char*)AllocMem);
			return res;
		}
		virtual Boolean RemoveSection(String^ SectionName) = IMAIMPConfigurationManager::RemoveSection
		{
			pin_ptr<const WCHAR> tmpMem = PtrToStringChars(SectionName);
			HRESULT res = inConfigFileMan->AIMP_Config_RemoveSection((PWCHAR)tmpMem, SectionName->Length);
			return SUCCEEDED(res);
		}
		virtual Boolean WriteParameter(String^ Section, String^ Name, Int32 val) = IMAIMPConfigurationManager::WriteParameter
		{
			pin_ptr<const WCHAR> tmpMem = PtrToStringChars(Section);
			pin_ptr<const WCHAR> tmpMem2 = PtrToStringChars(Name);
			HRESULT res = inConfigFileMan->AIMP_Config_WriteInteger((PWCHAR)tmpMem, (PWCHAR)tmpMem2, Section->Length, Name->Length, val);
			return SUCCEEDED(res);
		}
		virtual Boolean WriteParameter(String^ Section, String^ Name, String^ val) = IMAIMPConfigurationManager::WriteParameter
		{
			pin_ptr<const WCHAR> tmpMem = PtrToStringChars(Section);
			pin_ptr<const WCHAR> tmpMem2 = PtrToStringChars(Name);
			pin_ptr<const WCHAR> tmpMem3 = PtrToStringChars(val);
			HRESULT res = inConfigFileMan->AIMP_Config_WriteString((PWCHAR)tmpMem, (PWCHAR)tmpMem2, (PWCHAR)tmpMem3, Section->Length, Name->Length, val->Length);
			return SUCCEEDED(res);
		}

		virtual String^ GetConfigFilePath() = IMAIMPConfigurationManager::GetConfigFilePath
		{
			const int AllocSize = 2*1024;
			PWCHAR AllocMem = (PWCHAR)TConvAlloc::Allocate(AllocSize);
			AllocMem[AllocSize/2 - 1] = 0;
			inAIMPCtrl->AIMP_GetCfgPath(AllocMem, AllocSize/2 - 1);
			String^ res = gcnew String(AllocMem);
			TConvAlloc::Dispose((char*)AllocMem);
			return res;
		}

		// ================================================


		// ===================IMAIMPCoverArtManager=============================

		virtual System::Drawing::Bitmap^ LoadCoverArtForFile(String^ FileName, System::Drawing::Size sz)
		{
			pin_ptr<const WCHAR> tmpMem = PtrToStringChars(FileName);
			SIZE DrwSz;
			DrwSz.cx = sz.Width;
			DrwSz.cy = sz.Height;
			HBITMAP ldBmp = inCoverArtMan->GetCoverArtForFile((PWCHAR)tmpMem, &DrwSz);
			if (ldBmp > 0)
				return System::Drawing::Bitmap::FromHbitmap(System::IntPtr(ldBmp));
			return nullptr;
		}

		virtual System::Drawing::Bitmap^ LoadCoverArtForFile(String^ FileName, Int32 Width, Int32 Height)
		{
			return LoadCoverArtForFile(FileName, System::Drawing::Size(Width, Height));
		}

		virtual Boolean DrawCurrentCoverArt(System::Drawing::Graphics^ gr, System::Drawing::Rectangle rct)
		{
			RECT PRct;
			PRct.left = rct.Left;
			PRct.top = rct.Top;
			PRct.right = rct.Right;
			PRct.bottom = rct.Bottom;
			HRESULT hres = inCoverArtMan->CurrentCoverArtDraw((HDC)gr->GetHdc().ToPointer(), &PRct);
			return SUCCEEDED(hres);
		}

		virtual System::Drawing::Bitmap^ GetCurrentCoverArt(System::Drawing::Size sz)
		{
			System::Drawing::Bitmap^ Res = gcnew System::Drawing::Bitmap(sz.Width, sz.Height);
			System::Drawing::Graphics^ gr = System::Drawing::Graphics::FromImage(Res);
			if (DrawCurrentCoverArt(gr, System::Drawing::Rectangle(0, 0, sz.Width, sz.Height)))
			{
				delete gr;
				gr = nullptr;
			}
			else
			{
				delete gr;
				gr = nullptr;
				delete Res;
				Res = nullptr;
			}
			return Res;
		}

		virtual System::Drawing::Bitmap^ GetCurrentCoverArt(Int32 Width, Int32 Height)
		{
			return GetCurrentCoverArt(System::Drawing::Size(Width, Height));
		}

		virtual System::Drawing::Bitmap^ GetCurrentCoverArt()
		{
			System::Nullable<System::Drawing::Size> CurSz = CurrentCoverArtSize;
			if (!CurSz.HasValue)
				return nullptr;
			return GetCurrentCoverArt(CurSz.Value);
		}

		property virtual System::Nullable<System::Drawing::Size> CurrentCoverArtSize 
		{ 
			System::Nullable<System::Drawing::Size> get()
			{
				SIZE CurSz;
				HRESULT hres = inCoverArtMan->CurrentCoverArtGetSize(&CurSz);
				if (SUCCEEDED(hres))
					return System::Drawing::Size(CurSz.cx, CurSz.cy);

				return System::Nullable<System::Drawing::Size>();
			}
		}

		property virtual Boolean HasCoverArt
		{
			Boolean get()
			{
				SIZE CurSz;
				HRESULT hres = inCoverArtMan->CurrentCoverArtGetSize(&CurSz);
				return SUCCEEDED(hres);
			}
		}

		virtual Boolean ProportionalSizeCorrection(System::Drawing::Size% sz)
		{
			int wdth = sz.Width, hght = sz.Height;
			HRESULT hres = inCoverArtMan->CurrentCoverArtCorrectSizes(&wdth, &hght);
			if (SUCCEEDED(hres))
			{
				sz.Width = wdth;
				sz.Height = hght;
			}
			return SUCCEEDED(hres);
		}
		virtual Boolean ProportionalSizeCorrection(Int32% width, Int32% height)
		{
			int wdth = width, hght = height;
			HRESULT hres = inCoverArtMan->CurrentCoverArtCorrectSizes(&wdth, &hght);
			if (SUCCEEDED(hres))
			{
				width = wdth;
				height = hght;
			}
			return SUCCEEDED(hres);
		}

		// ================================================

		// ===================IMAIMPLanguageManager=============================

		virtual Boolean IsSectionExistLM(String^ Section) = IMAIMPLanguageManager::IsSectionExist
		{
			pin_ptr<const WCHAR> strData = PtrToStringChars(Section);
			HRESULT res = inLangFileMan->AIMP_Lang_IsSectionExists((PWCHAR)strData, Section->Length);
			return SUCCEEDED(res);
		}

		virtual Int32 GetLanguageVersion() = IMAIMPLanguageManager::GetLanguageVersion
		{
			return inLangFileMan->AIMP_Lang_Version();
		}

		virtual String^ ReadValueString(String^ Section, String^ Name) = IMAIMPLanguageManager::ReadValueString
		{
			const int AllocSize = 2*1024;
			pin_ptr<const WCHAR> tmpMem = PtrToStringChars(Section);
			pin_ptr<const WCHAR> tmpMem2 = PtrToStringChars(Name);
			PWCHAR AllocMem = (PWCHAR)TConvAlloc::Allocate(AllocSize);
			AllocMem[AllocSize/2 - 1] = 0;
			HRESULT hres = inLangFileMan->AIMP_Lang_ReadString((PWCHAR)tmpMem, (PWCHAR)tmpMem2, (PWCHAR)AllocMem, Section->Length, Name->Length, AllocSize/2 - 1);
			String^ res = nullptr;
			if (SUCCEEDED(hres))
				res = gcnew String(AllocMem);
			TConvAlloc::Dispose((char*)AllocMem);
			return res;
		}

		virtual String^ GetCurrentLanguage() = IMAIMPLanguageManager::GetCurrentLanguage
		{
			const int AllocSize = 128;
			PWCHAR AllocMem = (PWCHAR)TConvAlloc::Allocate(AllocSize);
			AllocMem[AllocSize/2 - 1] = 0;
			inAIMPCtrl->AIMP_GetLanguage(AllocMem, AllocSize/2 - 1);
			String^ res = gcnew String(AllocMem);
			TConvAlloc::Dispose((char*)AllocMem);
			return res;
		}

		virtual String^ GetLanguageFilePath() = IMAIMPLanguageManager::GetLanguageFilePath
		{
			const int AllocSize = 2*1024;
			PWCHAR AllocMem = (PWCHAR)TConvAlloc::Allocate(AllocSize);
			AllocMem[AllocSize/2 - 1] = 0;
			inAIMPExtended->AIMP_GetPath(AIMP_CFG_LNG, AllocMem, AllocSize/2 - 1);
			String^ res = gcnew String(AllocMem);
			TConvAlloc::Dispose((char*)AllocMem);
			return res + this->GetCurrentLanguage();
		}

		// ================================================

		//=======================IMAIMPEventManager======================

		virtual event MAIMPPlayerEventsDelegate^ SatusChangeEvent
		{
			[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)] 
			void add(MAIMPPlayerEventsDelegate^ f)
			{
				bool Before = this->inSatusChangeEventDelegate == nullptr;
				this->inSatusChangeEventDelegate = (MAIMPPlayerEventsDelegate^) Delegate::Combine(this->inSatusChangeEventDelegate, f);
				if (Before && this->inSatusChangeEventDelegate != nullptr)
					inLocalLinkToEventController->SatusChangeEvent += gcnew Action(this, &MAIMP2ControllerLocal::SatusChangeEventAction);
			}
			[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)]  
			void remove(MAIMPPlayerEventsDelegate^ f)
			{
				bool Before = this->inSatusChangeEventDelegate != nullptr;
				this->inSatusChangeEventDelegate = (MAIMPPlayerEventsDelegate^) Delegate::Remove(this->inSatusChangeEventDelegate, f);
				if (Before && this->inSatusChangeEventDelegate == nullptr)
					inLocalLinkToEventController->SatusChangeEvent -= gcnew Action(this, &MAIMP2ControllerLocal::SatusChangeEventAction);
			}
			void raise(Object^ sender, MAIMPPlayerEventArgs^ args)
			{
				MAIMPPlayerEventsDelegate^ tmp = this->inSatusChangeEventDelegate;
				if (tmp != nullptr)
				{
					try
					{
						tmp(sender, args);
					}
					catch (...) { }
				}
			}

		}
		virtual event MAIMPPlayerEventsDelegate^ PlayFileEvent
		{
			[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)] 
			void add(MAIMPPlayerEventsDelegate^ f)
			{
				bool Before = this->inPlayFileEventDelegate == nullptr;
				this->inPlayFileEventDelegate = (MAIMPPlayerEventsDelegate^) Delegate::Combine(this->inPlayFileEventDelegate, f);
				if (Before && this->inPlayFileEventDelegate != nullptr)
					inLocalLinkToEventController->PlayFileEvent += gcnew Action(this, &MAIMP2ControllerLocal::PlayFileEventAction);
			}
			[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)]  
			void remove(MAIMPPlayerEventsDelegate^ f)
			{
				bool Before = this->inPlayFileEventDelegate != nullptr;
				this->inPlayFileEventDelegate = (MAIMPPlayerEventsDelegate^) Delegate::Remove(this->inPlayFileEventDelegate, f);
				if (Before && this->inPlayFileEventDelegate == nullptr)
					inLocalLinkToEventController->PlayFileEvent -= gcnew Action(this, &MAIMP2ControllerLocal::PlayFileEventAction);
			}
			void raise(Object^ sender, MAIMPPlayerEventArgs^ args)
			{
				MAIMPPlayerEventsDelegate^ tmp = this->inPlayFileEventDelegate;
				if (tmp != nullptr)
				{
					try
					{
						tmp(sender, args);
					}
					catch (...) { }
				}
			}

		}
		virtual event MAIMPPlayerEventsDelegate^ InfoUpdateEvent
		{
			[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)] 
			void add(MAIMPPlayerEventsDelegate^ f)
			{
				bool Before = this->inInfoUpdateEventDelegate == nullptr;
				this->inInfoUpdateEventDelegate = (MAIMPPlayerEventsDelegate^) Delegate::Combine(this->inInfoUpdateEventDelegate, f);
				if (Before && this->inInfoUpdateEventDelegate != nullptr)
					inLocalLinkToEventController->InfoUpdateEvent += gcnew Action(this, &MAIMP2ControllerLocal::InfoUpdateEventAction);
			}
			[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)]  
			void remove(MAIMPPlayerEventsDelegate^ f)
			{
				bool Before = this->inInfoUpdateEventDelegate != nullptr;
				this->inInfoUpdateEventDelegate = (MAIMPPlayerEventsDelegate^) Delegate::Remove(this->inInfoUpdateEventDelegate, f);
				if (Before && this->inInfoUpdateEventDelegate == nullptr)
					inLocalLinkToEventController->InfoUpdateEvent -= gcnew Action(this, &MAIMP2ControllerLocal::InfoUpdateEventAction);
			}
			void raise(Object^ sender, MAIMPPlayerEventArgs^ args)
			{
				MAIMPPlayerEventsDelegate^ tmp = this->inInfoUpdateEventDelegate;
				if (tmp != nullptr)
				{
					try
					{
						tmp(sender, args);
					}
					catch (...) { }
				}
			}

		}
		virtual event MAIMPPlayerEventsDelegate^ PlayerStateChangedEvent
		{
			[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)] 
			void add(MAIMPPlayerEventsDelegate^ f)
			{
				bool Before = this->inPlayerStateChangedEventDelegate == nullptr;
				this->inPlayerStateChangedEventDelegate = (MAIMPPlayerEventsDelegate^) Delegate::Combine(this->inPlayerStateChangedEventDelegate, f);
				if (Before && this->inPlayerStateChangedEventDelegate != nullptr)
					inLocalLinkToEventController->PlayerStateChangedEvent += gcnew Action(this, &MAIMP2ControllerLocal::PlayerStateChangedEventAction);
			}
			[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)]  
			void remove(MAIMPPlayerEventsDelegate^ f)
			{
				bool Before = this->inPlayerStateChangedEventDelegate != nullptr;
				this->inPlayerStateChangedEventDelegate = (MAIMPPlayerEventsDelegate^) Delegate::Remove(this->inPlayerStateChangedEventDelegate, f);
				if (Before && this->inPlayerStateChangedEventDelegate == nullptr)
					inLocalLinkToEventController->PlayerStateChangedEvent -= gcnew Action(this, &MAIMP2ControllerLocal::PlayerStateChangedEventAction);
			}
			void raise(Object^ sender, MAIMPPlayerEventArgs^ args)
			{
				MAIMPPlayerEventsDelegate^ tmp = this->inPlayerStateChangedEventDelegate;
				if (tmp != nullptr)
				{
					try
					{
						tmp(sender, args);
					}
					catch (...) { }
				}
			}

		}
		virtual event MAIMPPlayerEventsDelegate^ EffectChangedEvent
		{
			[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)] 
			void add(MAIMPPlayerEventsDelegate^ f)
			{
				bool Before = this->inEffectChangedEventDelegate == nullptr;
				this->inEffectChangedEventDelegate = (MAIMPPlayerEventsDelegate^) Delegate::Combine(this->inEffectChangedEventDelegate, f);
				if (Before && this->inEffectChangedEventDelegate != nullptr)
					inLocalLinkToEventController->EffectChangedEvent += gcnew Action(this, &MAIMP2ControllerLocal::EffectChangedEventAction);
			}
			[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)]  
			void remove(MAIMPPlayerEventsDelegate^ f)
			{
				bool Before = this->inEffectChangedEventDelegate != nullptr;
				this->inEffectChangedEventDelegate = (MAIMPPlayerEventsDelegate^) Delegate::Remove(this->inEffectChangedEventDelegate, f);
				if (Before && this->inEffectChangedEventDelegate == nullptr)
					inLocalLinkToEventController->EffectChangedEvent -= gcnew Action(this, &MAIMP2ControllerLocal::EffectChangedEventAction);
			}
			void raise(Object^ sender, MAIMPPlayerEventArgs^ args)
			{
				MAIMPPlayerEventsDelegate^ tmp = this->inEffectChangedEventDelegate;
				if (tmp != nullptr)
				{
					try
					{
						tmp(sender, args);
					}
					catch (...) { }
				}
			}

		}
		virtual event MAIMPPlayerEventsDelegate^ EQChangedEvent
		{
			[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)] 
			void add(MAIMPPlayerEventsDelegate^ f)
			{
				bool Before = this->inEQChangedEventDelegate == nullptr;
				this->inEQChangedEventDelegate = (MAIMPPlayerEventsDelegate^) Delegate::Combine(this->inEQChangedEventDelegate, f);
				if (Before && this->inEQChangedEventDelegate != nullptr)
					inLocalLinkToEventController->EQChangedEvent += gcnew Action(this, &MAIMP2ControllerLocal::EQChangedEventAction);
			}
			[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)]  
			void remove(MAIMPPlayerEventsDelegate^ f)
			{
				bool Before = this->inEQChangedEventDelegate != nullptr;
				this->inEQChangedEventDelegate = (MAIMPPlayerEventsDelegate^) Delegate::Remove(this->inEQChangedEventDelegate, f);
				if (Before && this->inEQChangedEventDelegate == nullptr)
					inLocalLinkToEventController->EQChangedEvent -= gcnew Action(this, &MAIMP2ControllerLocal::EQChangedEventAction);
			}
			void raise(Object^ sender, MAIMPPlayerEventArgs^ args)
			{
				MAIMPPlayerEventsDelegate^ tmp = this->inEQChangedEventDelegate;
				if (tmp != nullptr)
				{
					try
					{
						tmp(sender, args);
					}
					catch (...) { }
				}
			}

		}
		virtual event MAIMPPlayerEventsDelegate^ TrackPosChangedEvent
		{
			[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)] 
			void add(MAIMPPlayerEventsDelegate^ f)
			{
				bool Before = this->inTrackPosChangedEventDelegate == nullptr;
				this->inTrackPosChangedEventDelegate = (MAIMPPlayerEventsDelegate^) Delegate::Combine(this->inTrackPosChangedEventDelegate, f);
				if (Before && this->inTrackPosChangedEventDelegate != nullptr)
					inLocalLinkToEventController->TrackPosChangedEvent += gcnew Action(this, &MAIMP2ControllerLocal::TrackPosChangedEventAction);
			}
			[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)]  
			void remove(MAIMPPlayerEventsDelegate^ f)
			{
				bool Before = this->inTrackPosChangedEventDelegate != nullptr;
				this->inTrackPosChangedEventDelegate = (MAIMPPlayerEventsDelegate^) Delegate::Remove(this->inTrackPosChangedEventDelegate, f);
				if (Before && this->inTrackPosChangedEventDelegate == nullptr)
					inLocalLinkToEventController->TrackPosChangedEvent -= gcnew Action(this, &MAIMP2ControllerLocal::TrackPosChangedEventAction);
			}
			void raise(Object^ sender, MAIMPPlayerEventArgs^ args)
			{
				MAIMPPlayerEventsDelegate^ tmp = this->inTrackPosChangedEventDelegate;
				if (tmp != nullptr)
				{
					try
					{
						tmp(sender, args);
					}
					catch (...) { }
				}
			}
		}


		virtual MAIMPPlayerEventsDelegate^ GetInvocation(MAIMPPlayerEventsDelegate^ dlg)
		{
			if (inIsCrossDomain)
			{
				MAIMPSafeCorssDomainCaller^ res = gcnew MAIMPSafeCorssDomainCaller(dlg, inAssocPluginUID);
				return gcnew MAIMPPlayerEventsDelegate(res, &MAIMPSafeCorssDomainCaller::CallFunction);
			}
			return dlg;
		}

	private:
		MAIMPPlayerEventsDelegate^ inSatusChangeEventDelegate;
		MAIMPPlayerEventsDelegate^ inPlayFileEventDelegate;
		MAIMPPlayerEventsDelegate^ inInfoUpdateEventDelegate;
		MAIMPPlayerEventsDelegate^ inPlayerStateChangedEventDelegate;
		MAIMPPlayerEventsDelegate^ inEffectChangedEventDelegate;
		MAIMPPlayerEventsDelegate^ inEQChangedEventDelegate;
		MAIMPPlayerEventsDelegate^ inTrackPosChangedEventDelegate;


		void SatusChangeEventAction()
		{
			SatusChangeEvent(this, MAIMPPlayerEventArgs::StatusChange);
		}
		void PlayFileEventAction()
		{
			PlayFileEvent(this, MAIMPPlayerEventArgs::PalyFile);
		}
		void InfoUpdateEventAction()
		{
			InfoUpdateEvent(this, MAIMPPlayerEventArgs::InfoUpdate);
		}
		void PlayerStateChangedEventAction()
		{
			PlayerStateChangedEvent(this, MAIMPPlayerEventArgs::PlayerState);
		}
		void EffectChangedEventAction()
		{
			EffectChangedEvent(this, MAIMPPlayerEventArgs::EffectChange);
		}
		void EQChangedEventAction()
		{
			EQChangedEvent(this, MAIMPPlayerEventArgs::EQChange);
		}
		void TrackPosChangedEventAction()
		{
			TrackPosChangedEvent(this, MAIMPPlayerEventArgs::TrackPosChange);
		}



	public:
		[System::Runtime::CompilerServices::MethodImplAttribute(System::Runtime::CompilerServices::MethodImplOptions::Synchronized)] 
		virtual void SafetyUnsubscribeFromEvents()
		{
			inLocalLinkToEventController->SatusChangeEvent -= gcnew Action(this, &MAIMP2ControllerLocal::SatusChangeEventAction);
			inLocalLinkToEventController->PlayFileEvent -= gcnew Action(this, &MAIMP2ControllerLocal::PlayFileEventAction);
			inLocalLinkToEventController->InfoUpdateEvent -= gcnew Action(this, &MAIMP2ControllerLocal::InfoUpdateEventAction);
			inLocalLinkToEventController->PlayerStateChangedEvent -= gcnew Action(this, &MAIMP2ControllerLocal::PlayerStateChangedEventAction);
			inLocalLinkToEventController->EffectChangedEvent -= gcnew Action(this, &MAIMP2ControllerLocal::EffectChangedEventAction);
			inLocalLinkToEventController->EQChangedEvent -= gcnew Action(this, &MAIMP2ControllerLocal::EQChangedEventAction);
			inLocalLinkToEventController->TrackPosChangedEvent -= gcnew Action(this, &MAIMP2ControllerLocal::TrackPosChangedEventAction);

			inSatusChangeEventDelegate = nullptr;
			inPlayFileEventDelegate = nullptr;
			inInfoUpdateEventDelegate = nullptr;
			inPlayerStateChangedEventDelegate = nullptr;
			inEffectChangedEventDelegate = nullptr;
			inEQChangedEventDelegate = nullptr;
			inTrackPosChangedEventDelegate = nullptr;
		}


	public:
		~MAIMP2Controller()
		{
			SafetyUnsubscribeFromEvents();

			if (inOptFrameManager != nullptr)
			{
				delete inOptFrameManager;
				inOptFrameManager = nullptr;
			}
			if (inMenuManager != nullptr)
			{
				delete inMenuManager;
				inMenuManager = nullptr;
			}
			if (inEqualizerObj != nullptr)
			{
				delete inEqualizerObj;
				inEqualizerObj = nullptr;
			}
			if (inPlsManager != nullptr)
			{
				delete inPlsManager;
				inPlsManager = nullptr;
			}
			if (inCoverArtMan != NULL)
			{
				inCoverArtMan->Release();
				inCoverArtMan = NULL;
			}
			if (inAIMPPlsMan != NULL)
			{
				inAIMPPlsMan->Release();
				inAIMPPlsMan = NULL;
			}
			if (inAIMPExtended != NULL)
			{
				inAIMPExtended->Release();
				inAIMPExtended = NULL;
			}
			if (inLangFileMan != NULL)
			{
				inLangFileMan->Release();
				inLangFileMan = NULL;
			}
			if (inConfigFileMan != NULL)
			{
				inConfigFileMan->Release();
				inConfigFileMan = NULL;
			}
			if (inPlayerCtrl != NULL)
			{
				inPlayerCtrl->Release();
				inPlayerCtrl = NULL;
			}
			if (inAIMPCtrl != NULL)
			{
				inAIMPCtrl->Release();
				inAIMPCtrl = NULL;
			}

			inLocalLinkToEventController = nullptr;
		}

	public:
		property virtual IAIMP2Controller* AController
		{
			IAIMP2Controller* get()
			{
				return inAIMPCtrl;
			}
		}

	private:
		IAIMP2Controller* inAIMPCtrl;
		IAIMP2Player* inPlayerCtrl;
		IAIMPConfigFile* inConfigFileMan;
		IAIMPLanguageFile* inLangFileMan;
		IAIMP2Extended* inAIMPExtended;
		IAIMP2PlaylistManager2* inAIMPPlsMan;
		IAIMP2CoverArtManager* inCoverArtMan;

		MAIMPEqualizer^ inEqualizerObj;
		MAIMPPlaylistManager^ inPlsManager;
		MAIMPMenuManager^ inMenuManager;
		MAIMPOptionFramesManager^ inOptFrameManager;

		bool inIsCrossDomain;
		int inDomainID;
		int inAssocPluginUID;

		CentralizedEventController^ inLocalLinkToEventController;
	};



	private ref class MAIMP2ControllerInitializer: public System::MarshalByRefObject
	{
	public:
		static String^ TypeName = "dotNetInteropPlugin.PluginDev.MAIMP2ControllerInitializer";

		MAIMP2Controller<StaticSingleThreadAllocator>^ CreateWithStaticAllocator(IAIMP2Controller* Ctrl, int PlgUID, int AppDomainID, bool CrossDmn, CentralizedEventController^ evCtrl)
		{
			return gcnew MAIMP2Controller<StaticSingleThreadAllocator>(Ctrl, PlgUID, AppDomainID, CrossDmn, evCtrl);
		}
	};
}
}